全部例程

WWDG -窗口看门狗

W55MH32

更新于 2026年9月8日

本篇总结

配置 WWDG 窗口看门狗,演示中断喂狗防复位 + 不喂狗触发系统复位两种场景。

🔑 KEIL MDK、UART调试、WWDG

硬件平台:本例程基于 W55MH32L-EVB 开发板运行。

采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W55MH32L-EVB

本示例演示 W55MH32 的窗口看门狗(WWDG)外设。与独立看门狗(IWDG)不同,WWDG 要求计数器值必须在一个窗口范围内(下限 0x40 ~ 上限 WR)才能喂狗——太早或太晚都会触发复位。本例程演示两种场景:(1) 通过提前唤醒中断(EWI)在窗口内喂狗,防止复位;(2) 故意不喂狗,观察 WWDG 触发系统复位。

超时时间公式

Twwdg = TPCLK1 × 4096 × 2WDGTB × (T[5:0] + 1)
当 PCLK1 = 36MHz、WDGTB = 3(分频 8)时,计数器减一格约 910μs,最大超时约 58.3ms。

前置工具准备

开始本例程前,请确认以下工具已安装:

硬件连接

WWDG 为芯片内部外设,无需外部引脚。仅需一根 Micro USB 线连接开发板 DEBUG USB 口到 PC,通过 USART1 串口日志观察看门狗触发与喂狗行为。

外设总线说明
WWDGAPB1窗口看门狗,计数器时钟 = PCLK1/4096/2^WDGTB
USART1APB2PA9(TX) / PA10(RX),板载 USB 转串口

工程结构

工程位于 SDK 内 1.SDK\ModuleDemo\WWDG\,核心文件如下:

WWDG/
├── USER/
│   ├── main.c            ← WWDG初始化 + 中断喂狗/复位测试 + printf 重定向
│   ├── w55mh32_conf.h    ← 外设头文件集中包含
│   ├── w55mh32_it.c/h    ← 中断服务函数
│   ├── system_w55mh32.c/h← 系统初始化(时钟配置)
│   └── startup_w55mh32.s ← 启动文件
└── SYSTEM/
    └── delay/
        └── delay.c/h      ← SysTick 延时函数

步骤1:USART 初始化(打印日志)

文件:USER/main.c | 函数:UART_Configuration() — 配置 USART1 用于打印时钟信息与看门狗日志。

void UART_Configuration(uint32_t bound)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    /* 开启外设时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    /* PA9: USART1_TX → 复用推挽输出 */
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* PA10: USART1_RX → 浮空输入 */
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* USART 参数:115200 / 8N1 / 全双工 */
    USART_InitStructure.USART_BaudRate            = bound;
    USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits            = USART_StopBits_1;
    USART_InitStructure.USART_Parity              = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;

    USART_Init(USART_TEST, &USART_InitStructure);
    USART_Cmd(USART_TEST, ENABLE);
}

四步完成:开时钟 → GPIO 引脚模式配置(TX 复用推挽,RX 浮空输入)→ USART 参数配置(115200 / 8N1)→ 使能外设。串口仅用于打印日志,方便观察喂狗与复位行为。

步骤2:WWDG 初始化(预分频器 + 窗口值 + 计数器)

函数:WWDG_Configuration() — 开启 WWDG 时钟,设置预分频器为 8、窗口上限 0x5F、计数器初值 0x7F。

void WWDG_Configuration(void)
{
    /* 开启 WWDG 时钟(挂载在 APB1) */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);

    /* 预分频器 8:CNT_CK = PCLK1 / 4096 / 8 */
    WWDG_SetPrescaler(WWDG_Prescaler_8);

    /* 窗口上限 0x5F:计数器在 0x40~0x5F 之间喂狗才合法 */
    WWDG_SetWindowValue(0x5F);

    /* 使能 WWDG,初始计数器值 0x7F(最大值,T6 必须为 1) */
    WWDG_Enable(0x7F);
}

三个关键参数:预分频器决定计数器减一格的时间(T = TPCLK1 × 4096 × 2WDGTB);窗口上限 WR 决定合法喂狗区间(0x40 ~ WR);计数器初值 TR 必须大于 WR 且 T6 位为 1(即 0x41~0x7F)。

如果你想自定义

  • 改超时时间 → 修改 WWDG_SetPrescaler 参数为 WWDG_Prescaler_1/2/4/8,分频越大超时越长
  • 改窗口范围 → 修改 WWDG_SetWindowValue 参数(0x41~0x7F),值越小窗口越宽,喂狗时机越宽松

步骤3:NVIC 配置与 WWDG 中断服务函数

函数:NVIC_Configuration() + WWDG_IRQHandler() — 配置 WWDG 中断优先级,并在中断中重装计数器(喂狗)。

/* NVIC 中断优先级配置 */
void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel                   = WWDG_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 3;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/* WWDG 提前唤醒中断(EWI):计数器减到 0x40 时触发,此处喂狗防复位 */
void WWDG_IRQHandler(void)
{
    WWDG_SetCounter(0x7F);   /* 喂狗:重装计数器到最大值 */
    WWDG_ClearFlag();        /* 清除中断标志,等待下一次触发 */
}

当计数器递减到 0x40(下限)时,若使能了提前唤醒中断(EWI),将触发 WWDG_IRQHandler。在中断中调用 WWDG_SetCounter(0x7F) 重装计数器即可避免复位——这也是"死前中断"的由来:进了这个中断说明程序已偏离正常序列。

中断喂狗的代价

中断喂狗虽能防复位,但若程序主循环跑飞、只剩中断在喂,看门狗就失去了监控意义。生产环境中应在中断里加上"看主循环是否正常运行"的判断,而非无条件喂狗。

步骤4:主函数 — 中断喂狗测试

文件:USER/main.c | 函数:main() — 初始化 USART → 配置 WWDG → 清标志 → 配置 NVIC → 使能 WWDG 中断 → 进入死循环等中断喂狗。

int main(void)
{
    RCC_ClocksTypeDef clocks;

    delay_init();
    UART_Configuration(115200);
    RCC_GetClocksFreq(&clocks);

    printf("\n");
    printf("SYSCLK: %3.1fMhz, HCLK: %3.1fMhz, PCLK1: %3.1fMhz, "
           "PCLK2: %3.1fMhz, ADCCLK: %3.1fMhz\n",
           (float)clocks.SYSCLK_Frequency / 1000000,
           (float)clocks.HCLK_Frequency / 1000000,
           (float)clocks.PCLK1_Frequency / 1000000,
           (float)clocks.PCLK2_Frequency / 1000000,
           (float)clocks.ADCCLK_Frequency / 1000000);
    printf("WWDG Int Test.\n");
    printf("Interrupt Feed Dog\n");

    /* 配置 WWDG:预分频 8 / 窗口 0x5F / 计数器 0x7F */
    WWDG_Configuration();

    /* 使能提前唤醒中断 */
    WWDG_ClearFlag();
    NVIC_Configuration();
    WWDG_EnableIT();

    while (1);   /* 主循环空转,由 EWI 中断自动喂狗 */
}

程序启动后打印时钟信息与测试提示,随后配置 WWDG 并使能 EWI 中断。主循环虽为空转,但每次计数器减到 0x40 时中断触发并重装计数器,系统不会复位。

步骤5:WWDG 复位测试(对比实验)

将上面的 main() 中"使能中断"三行去掉,主循环不喂狗——计数器减到 0x40 后无中断喂狗,WWDG 触发系统复位,程序重启。

int main(void)
{
    RCC_ClocksTypeDef clocks;

    delay_init();
    UART_Configuration(115200);
    RCC_GetClocksFreq(&clocks);

    printf("\n");
    printf("SYSCLK: %3.1fMhz, HCLK: %3.1fMhz, PCLK1: %3.1fMhz, "
           "PCLK2: %3.1fMhz, ADCCLK: %3.1fMhz\n",
           (float)clocks.SYSCLK_Frequency / 1000000,
           (float)clocks.HCLK_Frequency / 1000000,
           (float)clocks.PCLK1_Frequency / 1000000,
           (float)clocks.PCLK2_Frequency / 1000000,
           (float)clocks.ADCCLK_Frequency / 1000000);
    printf("WWDG Reset Test.\n");

    /* 配置 WWDG:预分频 8 / 窗口 0x5F / 计数器 0x7F */
    WWDG_Configuration();

    /* 不使能中断、不喂狗 → 计数器减到 0x40 触发系统复位 */
    while (1);
}

与步骤4仅差三行(去掉 WWDG_ClearFlag / NVIC_Configuration / WWDG_EnableIT)。WWDG 使能后计数器开始递减,无喂狗则超时复位——串口会反复打印启动日志,形成"复位→打印→复位"的循环。

调试小技巧

若想在 WWDG_IRQHandler 中确认喂狗是否执行,可加一行 printf("[WWDG] Feed dog!\n");,串口会周期性打印该信息。

步骤6:printf 重定向

重写 fputc() 将 printf 输出重定向到 USART1,并自动在 \n 前补 \r 确保串口工具正确换行。

int fputc(int c, FILE *f)
{
    if (c == '\n')
        SER_PutChar('\r');         // 补回车,确保串口工具正确换行
    return (SER_PutChar(c));
}

int SER_PutChar(int ch)
{
    while (!USART_GetFlagStatus(USART_TEST, USART_FLAG_TC));
    USART_SendData(USART_TEST, (uint8_t)ch);
    return ch;
}

运行验证

打开 WIZ UartTool V1.0 ,选择开发板对应 COM 口,波特率 115200 / 8N1,打开串口。

WIZ UartTool 串口输出 — WWDG 窗口超时复位测试

中断喂狗测试(步骤4代码):按 RESET 启动后,串口打印一次时钟信息与 "Interrupt Feed Dog",之后系统不再复位,主循环空转。若在中断中加了 printf 调试,会周期性看到 "[WWDG] Feed dog!"。

SYSCLK: 72.0Mhz, HCLK: 72.0Mhz, PCLK1: 36.0Mhz, PCLK2: 72.0Mhz, ADCCLK: 36.0Mhz
WWDG Int Test.
Interrupt Feed Dog
[WWDG] Feed dog!
[WWDG] Feed dog!
[WWDG] Feed dog!
...
WIZ UartTool 串口输出 — WWDG 窗口超时复位测试

复位测试(步骤5代码):串口会反复打印启动日志——每次 WWDG 超时复位后程序从头运行,形成"复位→打印→复位"的循环。

WIZ UartTool 串口输出 — WWDG 提前喂狗触发复位测试
SSYSCLK: 72.0Mhz, HCLK: 72.0Mhz, PCLK1: 36.0Mhz, PCLK2: 72.0Mhz, ADCCLK: 36.0Mhz
WWDG Int Test.
Interrupt Feed Dog

SYSCLK: 72.0Mhz, HCLK: 72.0Mhz, PCLK1: 36.0Mhz, PCLK2: 72.0Mhz, ADCCLK: 36.0Mhz
WWDG Int Test.
Interrupt Feed Dog
SYSCLK: 72.0Mhz, ...

常见问题

Q: 复位测试中串口一直重复打印启动日志

这正是 WWDG 复位的预期表现——没有喂狗,计数器减到 0x40 后触发系统复位,程序从头运行。若想停止复位,回到步骤4的中断喂狗代码。

Q: 中断喂狗测试中 WWDG 仍触发复位

检查三步是否齐全:WWDG_ClearFlag() 清标志、NVIC_Configuration() 配中断优先级、WWDG_EnableIT() 使能 EWI 中断,缺一不可。另外确认 WWDG_IRQHandler 已在 w55mh32_it.cmain.c 中实现。

Q: 喂狗了但还是复位(窗口外喂狗)

WWDG 要求计数器值在 0x40 ~ 窗口上限(WR) 之间才能喂狗。若在计数器值大于 WR 时喂狗(太早),也会触发复位。本例 WR=0x5F,EWI 中断在计数器减到 0x40 时触发,正好在窗口内,不会出现此问题。若手动喂狗需注意时机。

Q: 超时时间太短,中断来不及处理

增大预分频器 WWDG_SetPrescaler 参数(如改为 WWDG_Prescaler_8),或降低 PCLK1 频率。超时时间 T = TPCLK1 × 4096 × 2WDGTB × (T[5:0]+1)。

Q: 串口无任何输出

确认 COM 口号正确、波特率 115200,下载后需按 RESET 键启动。若复位测试中日志一闪而过,可降低波特率或增大预分频器延长超时时间,便于观察。

总结

本文基于 W55MH32 的窗口看门狗(WWDG)演示了两种典型场景:

  • WWDG 配置:预分频器 8 / 窗口上限 0x5F / 计数器初值 0x7F,超时时间由 TPCLK1 × 4096 × 2WDGTB × (T[5:0]+1) 决定
  • 中断喂狗:使能 EWI 提前唤醒中断,计数器减到 0x40 时触发 WWDG_IRQHandler,在其中重装计数器防复位
  • 复位测试:不使能中断、不喂狗,WWDG 超时后触发系统复位,程序反复重启
  • 窗口概念:下限固定 0x40,上限由用户设置;计数器值必须在窗口内喂狗才合法,太早或太晚都会复位

下载本章例程

我们提供完整的工程文件以及配套开发板,方便你随时测试,快速完成产品开发:

开发环境: Keil MDK5 配套开发板

Andy

W5500 技术支持专家